package com.dyava.dycache.guava;

import com.dyava.dycache.core.*;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by jjliu on 2020/4/8.
 */
public class GuavaCacheEngine extends CacheEngine {

    ConcurrentHashMap<String,Cache<String,Object>> cacheMap = new ConcurrentHashMap<>();

    ConcurrentHashMap<String, ReentrantLock> lockMap = new ConcurrentHashMap<>();

    protected Cache<String,Object> _obtainCacheWrap(CacheEngineContext cacheEngineContext,CacheDefined cacheDefined) {
        Cache<String,Object> cache = cacheMap.get(cacheDefined.getName());
        if(cache==null){
            cache = CacheBuilder.newBuilder().expireAfterWrite(generateExpire(cacheEngineContext,cacheDefined), TimeUnit.MILLISECONDS).build();
            Cache<String,Object> _cache = cacheMap.putIfAbsent(cacheDefined.getName(),cache);
            if(_cache != null){ //被别人放入
                cache = cacheMap.get(cacheDefined.getName());
            }
        }
        return cache;
    }

    @Override
    protected String getCacheVersion(CacheEngineContext cacheEngineContext, CacheDefined cacheDefined, Object id) {
        if(cacheDefined.isHasVersion()){
            CacheWrap cw = getCacheWrap(cacheEngineContext,cacheDefined,id);
            return cw.getVersion();
        }
        return null;
    }


    @Override
    protected  CacheWrap getCacheWrap(CacheEngineContext cacheEngineContext,CacheDefined cacheDefined, Object id) {
        Cache<String,Object> cache = _obtainCacheWrap(cacheEngineContext,cacheDefined);
        CacheWrap cw = (CacheWrap)cache.getIfPresent(id.toString());
//        if(cw==null){
//            cw = wrap(cacheEngineContext,cacheDefined,null);
//        }
        return cw;
    }

    @Override
    protected  CacheWrap waitCacheWrap(CacheEngineContext cacheEngineContext, CacheDefined cacheDefined, Object id) {
        String key = generateKey(cacheEngineContext,cacheDefined,id);
        ReentrantLock lock = lockMap.get(key);
        if(lock!=null){
            lock.lock();
            try{
                CacheWrap cw = getCacheWrap(cacheEngineContext,cacheDefined,id);
                if(cw==null){ //此时应该有值
                    throw new CacheRuntimeException("没有获得缓存");
                }
                return cw;
            }finally {
                lock.unlock();
            }
        }else{  //抽风，没有拿到锁，看看有没有值
            CacheWrap cw = getCacheWrap(cacheEngineContext,cacheDefined,id);
            if(cw==null){ //此时应该有值
                throw new CacheRuntimeException("没有获得缓存");
            }
            return cw;
        }
    }

    @Override
    protected void deleteCache(CacheEngineContext cacheEngineContext,CacheDefined cacheDefined, Object id) {
        Cache<String,Object> cache = _obtainCacheWrap(cacheEngineContext,cacheDefined);
        CacheWrap cw = (CacheWrap)cache.getIfPresent(id.toString());
        if(cw!=null){
            cw.setExpireAt(System.currentTimeMillis());
        }
    }

    @Override
    protected  void saveCacheWrap(CacheEngineContext cacheEngineContext,CacheDefined cacheDefined, Object id, CacheWrap cacheWrap) {
        Cache<String,Object> cache = _obtainCacheWrap(cacheEngineContext,cacheDefined);
        cache.put(id.toString(),cacheWrap);
    }

    @Override
    protected boolean tryLock(CacheEngineContext cacheEngineContext,CacheDefined cacheDefined, Object id) {
        String key = generateKey(cacheEngineContext,cacheDefined,id);
        ReentrantLock lock = lockMap.get(key);
        if(lock==null){
            lock = new ReentrantLock();
            Object _lock = lockMap.putIfAbsent(key,lock);
            if(_lock !=null ){ //被别人放入
                return false;
            }else{
                if(!lock.tryLock()){//lockMap放入就实现了原子性，此时的lock应该是独占的
                    throw new CacheRuntimeException("没有获得锁");
                };
                return true;
            }
        }
        return false;
    }

    @Override
    protected void unLock(CacheEngineContext cacheEngineContext, CacheDefined cacheDefined, Object id) {
        String key = generateKey(cacheEngineContext,cacheDefined,id);
        ReentrantLock lock = lockMap.get(key);
        if(lock!=null){
            lock.unlock();
            lockMap.remove(key);
        }
    }
}
